package vip.xiaomaoxiaoke.mpassistant.util;


import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import vip.xiaomaoxiaoke.mpassistant.annotation.Group;
import vip.xiaomaoxiaoke.mpassistant.annotation.Wrapper;
import vip.xiaomaoxiaoke.mpassistant.annotation.WrapperGroups;
import vip.xiaomaoxiaoke.mpassistant.enums.ConditionType;
import vip.xiaomaoxiaoke.mpassistant.enums.WrapperType;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.function.Consumer;


public class WrapperUtil {

    /**
     * 接口主要是读取类上的注解，生成一个QueryWrapper<T>类型的对象
     * @param entity 资源实体类，被添加了Wrapper注解的类，
     * @param  tClass 目标类的class类型，作用是为了由于mybatis传递的查询对象QueryWrapper必须是PO类型，而前端传递的参数我们要使用dto接收，因此使用该参数来声明PO的class类型
     * @return  返回一个tClass类型的 QueryWrapper对象
     * */
    public static <T> QueryWrapper<T> buildQueryWrapper(Object entity, Class<T> tClass) {
        // 初始化QueryWrapper对象
        QueryWrapper<T> queryWrapper = new QueryWrapper<>();

        queryWrapper.eq("1",1);
        // 读取实体类的类消息
        Class<?> aClass = entity.getClass();
        //获取class类上添加的WrapperGroups消息，一般是没有的
        WrapperGroups wrapperGroups = aClass.getAnnotation(WrapperGroups.class);
        // 获取实体类中的所有属性
        Field[] fields = aClass.getDeclaredFields();
        // 初始化一个属性容器，目前没有什么用，以后可以考虑性能优化
        Map<Wrapper, List<Field>> fieldMap = new LinkedHashMap<>();

        // 遍历每一个属性
        for (Field field : fields) {
            // 接触属性数据的访问限制
            field.setAccessible(true);
            // 获取当前属性中的Wrapper注解消息
            Wrapper wrapper = field.getAnnotation(Wrapper.class);

            // 判断该属性上是否被添加了Wrapper注解，如果没有添加则直接跳过该属性的处理逻辑
            if (wrapper == null)
                continue;
            try {
                // 获取该对象中的当前遍历的属性的指
                Object value = field.get(entity);

                // 初始化当前属性的列名
                String columnName;

                // 获取该属性中的mybatis的TableField注解消息
                TableField tableField = field.getAnnotation(TableField.class);

                // 设置列名
                if (tableField != null && tableField.exist() && StringUtils.isNotBlank(tableField.value())) {
                    // 如果有该注解，并且该注解的value属性不为空，则当前列名设置为TableField注解的value
                    columnName = tableField.value();
                } else {
                    // 如果没有通过mybatis的TableField注解设置列名，这判断是否通过Wrapper注解设置了，如果有则直接使用Wrapper注解的列名，如果没有则根据属性名换成下划线命名法设置列名
                    columnName = getColumnName(field, wrapper);
                }

                // 根据条件判断是否需要加入条件，判断标准是注解提供的属性两个属性中，有是否判断null和空字符串的属性
                boolean condition = isCondition(wrapper, value);

                if (StringUtils.isBlank(wrapper.group())) {
                    // 判断是否分组，如果不分组直接加入queryWrapper的查询条件里
                    invokeMethod(queryWrapper, wrapper, value, columnName, condition);
                } else {
                    // 如果分组了
                    List<Field> fieldList = fieldMap.get(wrapper);
                    if (fieldList == null) {
                        fieldMap.put(wrapper, new LinkedList<Field>() {{
                            add(field);
                        }});
                    } else {
                        fieldList.add(field);
                        fieldMap.put(wrapper, fieldList);
                    }
                }
            } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
                e.printStackTrace();
            }
            field.setAccessible(false);
        }
        // 获取类上的分组注解，判断是否为空
        if (wrapperGroups != null && wrapperGroups.groups().length > 0) {
            // 如果不为空的话则循环每一个分组
            for (Group group : wrapperGroups.groups()) {
                // 统计所有的属性中，属于该分组的属性个数
                long count = Arrays.stream(fields).filter(field1 ->
                        field1.getAnnotation(Wrapper.class) != null
                                && group.group().equals(field1.getAnnotation(Wrapper.class).group())).count();
                // 如果当前分组的属性个数为空，则跳过当前分组
                if (count == 0) {
                    continue;
                }
                //统计所有的属性中，值有效的属性的个数
                long c = Arrays.stream(fields).filter(field -> {
                    try {
                        field.setAccessible(true);
                        boolean condition = isCondition(field.getAnnotation(Wrapper.class), field.get(entity));
                        field.setAccessible(false);
                        return condition;
                    } catch (IllegalAccessException e) {
                        throw new RuntimeException(e);
                    }
                }).count();
                // 判断有效值的个数是大于0
                if (c > 0) {
                    // 值有效的个数如果是大于0的：

                    Consumer<QueryWrapper<T>> consumer = i -> {
                        // 遍历所有的属性
                        for (int j = 0, fieldsLength = fields.length; j < fieldsLength; j++) {
                            // 获取当前属性
                            Field field = fields[j];
                            // 以及当前属性的Wrapper注解
                            Wrapper wrapper = field.getAnnotation(Wrapper.class);
                            // 如果当前属性的分组属于当前分组
                            if (wrapper != null && group.group().equals(wrapper.group())) {
                                try {
                                    field.setAccessible(true);
                                    Object value = field.get(entity);
                                    // 则加入构造查询条件
                                    invokeMethod(i, wrapper, value, getColumnName(field, wrapper), isCondition(wrapper, value));
                                    field.setAccessible(false);
                                    if (group.conditionType() == ConditionType.OR && j < fieldsLength - 1)
                                        // 如果组内链接条件是or，则构造or查询
                                        i.or();
                                } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
                                    e.printStackTrace();
                                }
                            }
                        }
                    };
                    // 继续构造查询条件
                    if (group.splicingType() == ConditionType.AND) {
                        queryWrapper.and(consumer);
                    } else if (group.splicingType() == ConditionType.OR) {
                        queryWrapper.or(consumer);
                    }
                }
            }
        }
        return queryWrapper;
    }

    private static boolean isCondition(Wrapper wrapper, Object value) {
        if (wrapper == null) return false;
        boolean ignoreNull = wrapper.ignoreNull();
        boolean ignoreBlank = wrapper.ignoreBlank();
        return getCondition(ignoreNull, ignoreBlank, value);
    }

    /**
     * 根据注解获取列名，如果wrapper注解设置了列名则直接使用wrapper注解设置的列名，否则则使用属性名改成下划线命名法
     * @param field 属性
     * @param wrapper 注解
     * @return 属性对应的mysql的列名
     * */
    private static String getColumnName(Field field, Wrapper wrapper) {
        return StringUtils.isNotBlank(wrapper.columnName())
                ? wrapper.columnName()
                : StringUtils.camelToUnderline(field.getName());
    }

    private static <T> void invokeMethod(QueryWrapper<T> queryWrapper, Wrapper annotation, Object value, String columnName, boolean condition) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
        Method method;
        if (annotation.value() == WrapperType.IN || annotation.value() == WrapperType.NOT_IN) {
            method = queryWrapper.getClass().getMethod(StringUtils.underlineToCamel(annotation.value().toString()),
                    boolean.class, Object.class, Collection.class);
            if (value instanceof String[]) {
                method.invoke(queryWrapper, condition, columnName, Arrays.asList((String[]) value));
            } else if (value instanceof List) {
                method.invoke(queryWrapper, condition, columnName, value);
            }else if (value instanceof String) {
                method.invoke(queryWrapper, condition, columnName, Arrays.asList(value.toString().split(",")));
            }
        } else {
            method = queryWrapper.getClass().getMethod(StringUtils.underlineToCamel(annotation.value().toString()),
                    boolean.class, Object.class, Object.class);
            method.invoke(queryWrapper, condition, columnName, value);
        }
    }

    private static boolean getCondition(boolean ignoreNull, boolean ignoreBlank, Object value) {
        if (value == null) {
            return !ignoreNull;
        } else {
            if (value instanceof String) {
                if (StringUtils.isBlank((String) value)) {
                    return !ignoreBlank;
                }
            }
            return true;
        }
    }



}
